#include "Task.hpp"   // 包含任务定义的头文件
#include <string>     // 包含C++标准库中的string类
#include <vector>     // 包含C++标准库中的vector容器
#include <cstdlib>    // 包含C标准库中的rand()和srand()函数
#include <ctime>      // 包含C标准库中的time()函数，用于生成随机数种子
#include <cassert>    // 包含C标准库中的assert()函数，用于断言
#include <unistd.h>   // 包含Unix标准库函数，如fork()、pipe()、close()等
#include <sys/stat.h> // 包含文件状态系统调用，如stat()等
#include <sys/wait.h> // 包含进程等待系统调用，如waitpid()等

const int processnum = 10; // 定义子进程的数量

std::vector<task_t> tasks; // 全局任务列表，用于存储任务函数

// 描述子进程的通信通道
class channel
{
public:
    channel(int cmdfd, int slaverid, const std::string &processname)
        : _cmdfd(cmdfd), 
          _slaverid(slaverid), 
          _processname(processname)
    {}
public:
    int _cmdfd;               // 子进程写入任务命令的文件描述符
    pid_t _slaverid;          // 子进程的PID
    std::string _processname; // 子进程的名字，方便打印日志
};

// 子进程执行的函数
void slaver()
{
    // 不断循环等待父进程发送命令
    while(true)
    {
        int cmdcode = 0;
        int n = read(0, &cmdcode, sizeof(int)); // 从标准输入读取命令，阻塞等待
        if(n == sizeof(int))
        {
            // 执行接收到的命令对应的任务
            std::cout <<"slaver say@ get a command: "<< getpid() << " : cmdcode: " <<  cmdcode << std::endl;
            if(cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();
        }
        if(n == 0) break; // 读取到EOF，退出循环
    }
}

// 初始化进程池，创建多个子进程
void InitProcessPool(std::vector<channel> *channels)
{
    std::vector<int> oldfds; // 存储已经创建的管道的写端文件描述符
    for(int i = 0; i < processnum; i++)
    {
        int pipefd[2]; // 临时管道数组，pipefd[0]为读端，pipefd[1]为写端
        int n = pipe(pipefd); // 创建管道
        assert(!n); // 确保管道创建成功
        (void)n;

        pid_t id = fork(); // 创建子进程
        if(id == 0) // 子进程执行的代码
        {
            std::cout << "child: " << getpid() << " close history fd: ";
            for(auto fd : oldfds) {
                std::cout << fd << " ";
                close(fd); // 关闭已经创建的旧管道写端
            }
            std::cout << "\n";

            close(pipefd[1]); // 关闭当前管道的写端
            dup2(pipefd[0], 0); // 将标准输入重定向到当前管道的读端
            close(pipefd[0]); // 关闭当前管道的读端
            slaver(); // 进入子进程执行函数
            std::cout << "process : " << getpid() << " quit" << std::endl;
            exit(0); // 子进程执行完毕后退出
        }
        // 父进程执行的代码
        close(pipefd[0]); // 父进程关闭当前管道的读端

        // 将新创建的管道写端和子进程信息加入到通道列表中
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1], id, name));
        oldfds.push_back(pipefd[1]); // 记录当前管道的写端

        sleep(1); // 休眠1秒，用于让子进程启动
    }
}

// 调试函数，打印每个子进程的通道信息
void Debug(const std::vector<channel> &channels)
{
    for(const auto &c : channels)
    {
        std::cout << c._cmdfd << " " << c._slaverid << " " << c._processname << std::endl;
    }
}

// 打印菜单
void Menu()
{
    std::cout << "################################################" << std::endl;
    std::cout << "# 1. 刷新日志             2. 刷新出来野怪        #" << std::endl;
    std::cout << "# 3. 检测软件是否更新      4. 更新用的血量和蓝量  #" << std::endl;
    std::cout << "#                         0. 退出               #" << std::endl;
    std::cout << "#################################################" << std::endl;
}

// 控制子进程，向其发送任务命令
void ctrlSlaver(const std::vector<channel> &channels)
{
    int which = 0; // 用于循环选择子进程
    while(true)     
    {
        int select = 0;
        Menu();
        std::cout << "Please Enter@ ";
        std::cin >> select; // 用户输入选择的任务编号

        if(select <= 0 || select >= 5) break; // 输入无效，退出循环
        int cmdcode = select - 1; // 将用户输入转换为任务命令

        // 选择当前子进程，并发送任务命令
        std::cout << "father say: " << " cmdcode: " <<
            cmdcode << " already sendto " << channels[which]._slaverid << " process name: " 
                << channels[which]._processname << std::endl;
        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode)); // 发送命令到子进程

        which++;
        which %= channels.size(); // 循环选择下一个子进程
    }
}

// 清理子进程，关闭通道并等待子进程结束
void QuitProcess(const std::vector<channel> &channels)
{
    for(const auto &c : channels){
        close(c._cmdfd); // 关闭每个通道的写端
        waitpid(c._slaverid, nullptr, 0); // 等待对应的子进程结束
    }
}

// 主函数
int main()
{
    LoadTask(&tasks); // 加载任务列表

    srand(time(nullptr)^getpid()^1023); // 初始化随机数种子

    std::vector<channel> channels; // 存储所有子进程的通信通道
    InitProcessPool(&channels); // 初始化进程池，创建子进程

    ctrlSlaver(channels); // 控制子进程执行任务

    QuitProcess(channels); // 退出时清理子进程

    return 0;
}
